package com.funcablaze.func.parser;

import com.funcablaze.component.Scene.Scene;
import com.funcablaze.component.Scene.Scene2D;
import com.funcablaze.component.Scene.Scene3D;
import com.funcablaze.component.control.Graphic;
import com.funcablaze.component.control.Graphic2D;
import com.funcablaze.component.control.Graphic3D;
import com.funcablaze.component.control.Theme;
import com.funcablaze.component.view.View;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class XML {
	
	public static class ThemeParser {
		/**
		 * 解析主题XML文件
		 * @param is 输入流
		 */
		public static void parser(InputStream is) throws IOException, ParserConfigurationException, SAXException {
			// 解析XML文档
			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
			// 获取根元素
			Element root = doc.getDocumentElement();
			if (!"Themes".equals(root.getTagName())) {
				throw new IllegalArgumentException("主题XML文件的根元素必须是 Themes");
			}
			for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
				if (node instanceof Element element) {
					if (!"Theme".equals(element.getTagName())) {
						throw new IllegalArgumentException("主题XML文件的元素 " + element.getTagName() + " 必须是Theme");
					}
					Map<String, String> attrs = new HashMap<>();
					String name = element.getAttribute("name");
					String type = element.getAttribute("type");
					for (Node attr = element.getFirstChild(); attr != null; attr = attr.getNextSibling()) {
						if (attr instanceof Element item) {
							if ("item".equals(item.getTagName())) {
								attrs.put(item.getAttribute("name"), item.getTextContent());
							} else {
								throw new IllegalArgumentException("主题XML文件的元素 " + element.getTagName() + " 必须是item");
							}
						}
					}
					Theme.apply(type, name, attrs);
				}
			}
			is.close();
		}
	}
	
	public static class SceneParser {
		
		public static final String ViewPath = "com.funcablaze.component.view.";
		
		/**
		 * 解析场景XML文件
		 * @param is 输入流
		 * @return Scene场景
		 */
		public static Scene parser(InputStream is) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ParserConfigurationException, SAXException {
			// 解析XML文档
			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
			// 获取根元素
			Element root = doc.getDocumentElement();
			Scene scene;
			Graphic graphic;
			switch (root.getAttribute("dim")) {
				case "2d", "2D" -> {
					scene = new Scene2D();
					graphic = new Graphic2D();
				}
				case "3d", "3D" -> {
					scene = new Scene3D();
					graphic = new Graphic3D();
				}
				default -> throw new IllegalArgumentException("场景XML文件的根元素没有指定是2D或3D，使用dim标注维度");
			}
			root.removeAttribute("dim");
			View view = loadFromElement(root);
			scene.setGraphic(graphic);
			scene.setRootView(view);
			is.close();
			return scene;
		}
		
		private static View loadFromElement(Element node) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
			Class<?> clazz;
			if (node.getTagName().contains(".")) {
				clazz = Class.forName(node.getTagName());
			} else {
				clazz = Class.forName(ViewPath + node.getTagName());
			}
			if (!View.class.isAssignableFrom(clazz)) {
				throw new IllegalArgumentException("场景XML文件的元素 " + node.getTagName() + " 必须继承自View");
			}
			
			View view = (View) clazz.getDeclaredConstructor().newInstance();
			
			// 设置属性
			Map<String, String> attrs = new HashMap<>();
			NamedNodeMap attributes = node.getAttributes();
			for (int i = 0; i < attributes.getLength(); i++) {
				Node attr = attributes.item(i);
				attrs.put(attr.getNodeName(), attr.getNodeValue());
			}
			View.applyAttributes(view, attrs);
			
			NodeList children = node.getChildNodes();
			for (int i = 0; i < children.getLength(); i++) {
				Node child = children.item(i);
				if (child instanceof Element element) {
					View j = loadFromElement(element);
					view.addChild(j);
				}
			}
			return view;
		}
	}
}
